﻿using CommandLine;
using CommandLine.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace ExcludeLines
{
	class Options
	{
		[Option('s', "source", Required = true,
		  HelpText = "Input file to be processed.")]
		public string InputFile { get; set; }

		[Option('o', "output", Required = false,
		  HelpText = "Output file to write the result. Otherwise it will be displayed on the screen")]
		public string OutputFile { get; set; }

		[Option('e', "exclude", Required = true,
		  HelpText = "Exlude any lines from this file")]
		public string ExcludeLinesFromThisFile { get; set; }

		[Option('c', "case", Required = false,
		  HelpText = "Specify if the comparison should be case sensitive")]
		public bool CaseSensitive { get; set; }

		[Option('p', "prefix", Required = false,
		  HelpText = "Specify a custom prefix that should be appended on every line of the source file when doing the search. This is useful i.e. if you want to define relative paths from a common folder")]
		public string MatchPrefix { get; set; }

		[Option('u', "prefix", Required = false,
		  HelpText = "Specify a custom suffix that should be appended on every line of the source file when doing the search. This is useful i.e. if you want to same extension to e.g. a list of file names")]
		public string MatchSuffix { get; set; }

		[OptionList('r', "regex", Required = false,
		  HelpText = "Specify a regex patter to exclude the lines. This parameter can be specified multiple times")]
		public IList<string> Regexes { get; set; }

		[HelpOption]
		public string GetUsage()
		{
			var help = new HelpText
			{
				Heading = new HeadingInfo("ExcludeLines", "1.0"),
				Copyright = new CopyrightInfo("DotNetWise", 2013),
				AdditionalNewLineAfterOption = true,
				AddDashesToOption = true
			};
			help.AddPreOptionsLine("Excludes some lines defined in a file from the source file");
			help.AddPreOptionsLine("Usage: app -s sourceFile -e excludeFile");
			help.AddOptions(this);
			return help;
		}

	}

	class Program
	{
		static int Main(string[] args)
		{
			var options = new Options();
			if (CommandLine.Parser.Default.ParseArguments(args, options))
			{
				if (!File.Exists(options.InputFile))
					Console.WriteLine("The input file '{0}' is not found", options.InputFile);

				if (!File.Exists(options.ExcludeLinesFromThisFile))
					Console.WriteLine("The file to exclude the lines from: '{0}' is not found", options.ExcludeLinesFromThisFile);

				var sourceLines = File.ReadAllLines(options.InputFile);
				var excludeLines = File.ReadAllLines(options.ExcludeLinesFromThisFile)
					.Where(l => !l.StartsWith("#")).ToArray();
				if (!string.IsNullOrEmpty(options.MatchPrefix))
					excludeLines = excludeLines.Select(l => l.StartsWith("~") ? l : options.MatchPrefix + l).ToArray();

				if (!string.IsNullOrEmpty(options.MatchSuffix))
					excludeLines = excludeLines.Select(l => l.StartsWith("~") ? l : l + options.MatchSuffix).ToArray();

				if (options.Regexes != null && options.Regexes.Any())
					foreach (var regex in options.Regexes)
					{
						Regex r;
						try
						{
							r = new Regex(regex);
						}
						catch (Exception ex)
						{
							Console.WriteLine("Regex pattern error: "+regex);
							Console.WriteLine(ex.Message);
							return 3;
						}
						sourceLines = sourceLines.Where(l => !r.Match(l).Success).ToArray();
					}

				var result = sourceLines.Except(excludeLines.Where(l=>!l.StartsWith("~")), options.CaseSensitive ? StringComparer.OrdinalIgnoreCase : StringComparer.CurrentCulture);
				var excludeFileRegexes = excludeLines.Where(l=>l.StartsWith("~"))
					.Select(l => l.Substring(1).Replace("{s}", Regex.Escape(options.MatchSuffix ?? "")).Replace("{p}", Regex.Escape(options.MatchPrefix ?? "")))
					.Select(l => new Regex(l, RegexOptions.IgnoreCase))
					.ToArray();
				foreach (var r in excludeFileRegexes)
				{
					result = result.Where(l => !r.Match(l).Success);
				}

				if (string.IsNullOrEmpty(options.OutputFile))
					Console.Write(result);
				else File.WriteAllLines(options.OutputFile, result);
				return 0;
			}
			return 2;
		}
	}
}
